package at.jku.ase.client;

import at.jku.ase.communication.SessionObject;
import at.jku.ase.communication.parsers.*;
import at.jku.ase.utils.*;

import javax.xml.stream.*;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientMain {
    public static void main(String... args) {
        if (args.length != 1) {
            System.out.println("Please specify a path to a document.");
            System.exit(0);
        }
        String pathSeparator = System.getProperty("file.separator");
        System.out.println("Given file: " + ClientMain.class.getResource(pathSeparator + args[0]).getPath());
        File message = new File(ClientMain.class.getResource(pathSeparator + args[0]).getPath());
        if (!message.isFile()) {
            System.out.println("Directory is not supported.\nPlease specify a document.");
            System.exit(0);
        }
        if (message.length() > 2097152L) {
            System.out.println("Please specify a document less than 2M");
            System.exit(0);
        }

        byte[] secretMessage = FileUtil.load(message);

        final Wrapper<Socket> socketWrapper = new Wrapper<Socket>();
        final Wrapper<XMLStreamReader> readerWrapper = new Wrapper<XMLStreamReader>();
        final Wrapper<XMLStreamWriter> writerWrapper = new Wrapper<XMLStreamWriter>();
        try {
            Socket socket = new Socket("127.0.0.1", 9090);
            socketWrapper.set(socket);

            XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
            writerWrapper.set(outputFactory.createXMLStreamWriter(socket.getOutputStream()));
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        } catch (IOException e2) {
            throw new RuntimeException(e2);
        } catch (XMLStreamException e) {
            throw new RuntimeException(e);
        }

        final XMLStreamWriter writer = writerWrapper.get();
        final SessionObject session = SessionObject.newInstance();
        final Socket socket = socketWrapper.get();

        XMLUtils.startXMLStream(writer);
        XMLUtils.writePublicKey(writer, session);

        System.out.println("Init xml stream parser...");
        final StanzaParser parser = initParser(secretMessage, writer, session);

        // Initial XML stream reader
        new OpThrowsException() {
            @Override
            public void op() throws Exception {
                XMLInputFactory inputFactory = XMLInputFactory.newInstance();
                readerWrapper.set(inputFactory.createXMLStreamReader(socket.getInputStream()));
            }
        }.execute();

        // Parsing incoming XML Stream
        new OpThrowsException() {
            @Override
            public void op() throws Exception {
                parser.parse(readerWrapper.get());
                socket.close();
            }

            public void cleanUp() {
                XMLUtils.endXMLStream(writer);
            }
        }.execute();

    }

    private static StanzaParser initParser(final byte[] secretMessage, final XMLStreamWriter writer, final SessionObject session) {
        final StanzaParser parser = new StanzaParser();
        parser.registerParser("publicKey", new PublicKeyParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                System.out.println("Generating AES key...");
                session.setSecretKey(CryptUtils.generateAESKey());
                System.out.println("Sending secret key to server");
                byte[] aesKey = CryptUtils.encryptSecretKey(session.getReceiverPublicKey(), session.getSecretKey());
                XMLUtils.writeEncryptedSecretKey(writer, aesKey);
                writer.flush();
                System.out.println("Sent secret key...");
            }
        }));
        parser.registerParser("secretKey", new SecretKeyParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                XMLUtils.startTransmission(writer);
                session.setData(CryptUtils.encryptData(secretMessage, session.getSecretKey()));
                XMLUtils.writeData(writer, session.getData());
                session.setSignature(CryptUtils.signData(session.getData(), session.getMyPrivateKey()));
                XMLUtils.writeSignature(writer, session.getSignature());
                XMLUtils.endTransmission(writer);
                writer.flush();
            }
        }));
        parser.registerParser("feedback", new FeedbackParser(session, new XMLResponse() {
            public void response() throws IOException, XMLStreamException {
                System.out.println("Signature Status: " + session.isSignValid());
                XMLUtils.endXMLStream(writer);
            }
        }));
        return parser;
    }


}
